#pragma once

#include <mqtt/client.h>
#include <unordered_set>

enum MQTT_EVENTS {
	EVT_ERROR = EVT_EXTERNAL_BEGIN,
	EVT_CONNECTED,
	EVT_CONNECT_LOSE,
	EVT_MSG_ARRIVED,
	EVT_MSG_DELIVERY_COMPLETE
};

SEVENT_BEGIN(EventMQTTError, EVT_ERROR)
SOUI::SStringW err_msg;
SEVENT_END()

SEVENT_BEGIN(EventMQTTConnected, EVT_CONNECTED)
SOUI::SStringW err_msg;
SEVENT_END()

SEVENT_BEGIN(EventMQTTConnectedLose, EVT_CONNECT_LOSE)
SOUI::SStringW err_msg;
SEVENT_END()

SEVENT_BEGIN(EventMQTTMsgArrived, EVT_MSG_ARRIVED)
mqtt::const_message_ptr msg;
SEVENT_END()

SEVENT_BEGIN(EventMQTTMsgDeliveryComplete, EVT_MSG_DELIVERY_COMPLETE)
mqtt::delivery_token_ptr token;
SEVENT_END()

class CMQTTClient : public mqtt::callback, public std::enable_shared_from_this<CMQTTClient> {

	void initEvent()
	{
		SNotifyCenter::getSingleton().addEvent(EVENTID(EventMQTTError));
		SNotifyCenter::getSingleton().addEvent(EVENTID(EventMQTTConnected));
		SNotifyCenter::getSingleton().addEvent(EVENTID(EventMQTTConnectedLose));
		SNotifyCenter::getSingleton().addEvent(EVENTID(EventMQTTMsgArrived));
		SNotifyCenter::getSingleton().addEvent(EVENTID(EventMQTTMsgDeliveryComplete));
	}

	void removeEvent()
	{
		SNotifyCenter::getSingleton().removeEvent(EventMQTTError::EventID);
		SNotifyCenter::getSingleton().removeEvent(EventMQTTConnected::EventID);
		SNotifyCenter::getSingleton().removeEvent(EventMQTTConnectedLose::EventID);
		SNotifyCenter::getSingleton().removeEvent(EventMQTTMsgArrived::EventID);
		SNotifyCenter::getSingleton().removeEvent(EventMQTTMsgDeliveryComplete::EventID);
	}

public:

	CMQTTClient();

	~CMQTTClient();

	bool Init(const char* url, const char* clientId);

	void Reset()
	{
		if (_client)
		{
			if (_client->is_connected())
				_client->disconnect();
			delete _client;
			_client = nullptr;
		}
	}
	/*template<typename ...Args>
	bool Subscribe(Args... args) {
		try {
			_client->subscribe(args...);
		}
		catch (const mqtt::persistence_exception& exc) {
			std::cout << "InitPubSub: " << exc.what();
			return false;
		}
		catch (const mqtt::exception& exc) {
			std::cout << "InitPubSub: " << exc.what();
			return false;
		}
		return true;
	}*/

	bool Subscribe(const std::string& topicFilter, int qos)
	{
		try {
			_client->subscribe(topicFilter, qos);
		}
		catch (const mqtt::persistence_exception& exc) {
			std::cout << "InitPubSub: " << exc.what();
			return false;
		}
		catch (const mqtt::exception& exc) {
			std::cout << "InitPubSub: " << exc.what();
			return false;
		}
		return true;
	}

	void Connect();

	void StartConnect();

	void SendMsg(const char* topic, std::string& msg);

	void DisConnect();

	mqtt::client* GetClient();

protected:

	void connected(const std::string& cause) override;

	void connection_lost(const std::string& cause) override;

	void message_arrived(mqtt::const_message_ptr msg) override;

	void delivery_complete(mqtt::delivery_token_ptr tok) override;

private:

	mqtt::client* _client = nullptr;

	mqtt::connect_options _opts;

	mqtt::ssl_options _sslopt;
	//std::recursive_mutex _mutex;
};
